/**
 *
 */
package de.cbf.cam.dsa.person.entities;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.cbf.cam.dsa.item.model.entities.Garment;
import de.cbf.cam.dsa.item.model.entities.Item;
import de.cbf.cam.dsa.item.model.entities.constants.WearPlaces;
import de.cbf.util.uuidheader.entities.UUIDHeader;
import de.cbf.util.uuidheader.exceptions.EntityMergeException;

/**
 * @author salter
 * class to maintain the items a person has
 */
public class PersonItemsDSA extends UUIDHeader{
	private static final Log logger = LogFactory.getLog(PersonItemsDSA.class);
	/**
	 * the clothes a person wears<br>
	 * this are also things the person is carrying see personItems<br>
	 * key(String) is wear place/zone, list is garments
	 */
	protected Map<String,List<Garment>> personClothing;

	/**
	 * the items a person has got
	 */
	protected List<Item> personItems;



	public PersonItemsDSA() {
		super();
	}

	protected void init() {
		super.init();
		personItems 	= new ArrayList<Item>();
		personClothing	= new HashMap<String,List<Garment>>();
		initPersonClothingWithWearPlaces(personClothing);
	}

	protected void initPersonClothingWithWearPlaces(Map<String,List<Garment>> clothing) {
		//add the known wear places and make lists for them
		//reason is to have a good clothes look describing every found wear place
		clothing.put(WearPlaces.HAIR					, new ArrayList<Garment>());
		clothing.put(WearPlaces.HEAD					, new ArrayList<Garment>());
		clothing.put(WearPlaces.NECK					, new ArrayList<Garment>());
		clothing.put(WearPlaces.SHOULDER_LEFT			, new ArrayList<Garment>());
		clothing.put(WearPlaces.SHOULDER_RIGHT			, new ArrayList<Garment>());
		clothing.put(WearPlaces.CHEST					, new ArrayList<Garment>());
		clothing.put(WearPlaces.STOMACH					, new ArrayList<Garment>());
		clothing.put(WearPlaces.BACK_UPPER				, new ArrayList<Garment>());
		clothing.put(WearPlaces.BACK_LOWER				, new ArrayList<Garment>());
		clothing.put(WearPlaces.BELT					, new ArrayList<Garment>());
		clothing.put(WearPlaces.HIPS					, new ArrayList<Garment>());

		clothing.put(WearPlaces.LEG_UPPER_LEFT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_UPPER_LEFT_BACK		, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_LOWER_LEFT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_LOWER_LEFT_BACK		, new ArrayList<Garment>());
		clothing.put(WearPlaces.FOOT_LEFT				, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_UPPER_RIGHT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_UPPER_RIGHT_BACK	, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_LOWER_RIGHT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.LEG_LOWER_RIGHT_BACK	, new ArrayList<Garment>());
		clothing.put(WearPlaces.FOOT_RIGHT				, new ArrayList<Garment>());

		clothing.put(WearPlaces.ARM_UPPER_LEFT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_UPPER_LEFT_BACK		, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_LOWER_LEFT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_LOWER_LEFT_BACK		, new ArrayList<Garment>());
		clothing.put(WearPlaces.HAND_LEFT			 	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_UPPER_RIGHT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_UPPER_RIGHT_BACK	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_LOWER_RIGHT_FRONT	, new ArrayList<Garment>());
		clothing.put(WearPlaces.ARM_LOWER_RIGHT_BACK	, new ArrayList<Garment>());
		clothing.put(WearPlaces.HAND_RIGHT			 	, new ArrayList<Garment>());
	}

	@Override
	protected void doMergeDataFrom(UUIDHeader entity, boolean i_am_older)
			throws EntityMergeException {
		if (entity instanceof PersonItemsDSA) {
			PersonItemsDSA other = (PersonItemsDSA) entity;
			if(i_am_older){
				this.personClothing.clear();
				this.personClothing.putAll(other.personClothing);
				this.personItems.clear();
				this.personItems.addAll(other.personItems);
			}
		}else{
			String mergeEntityClass = entity == null? "null":entity.getClass().getName();
			String errorMsg ="EntityToMerge "+mergeEntityClass+" does not fit class:"+this.getClass().getName();
			throw new EntityMergeException(errorMsg);
		}
	}

	public String itemString() {
		return personItems.toString();
	}

	public String getClothesLook() {
		List<Garment> seenGarments = new ArrayList<Garment>();
		//iterate all wear places and collect item with highest layer in list, that items are seen, rest is hidden
		Set<String> wearPlaces = personClothing.keySet();
		for (String wearPlace : wearPlaces) {
			List<Garment> garments = personClothing.get(wearPlace);
			if(garments != null && ! garments.isEmpty()){
				Garment topGarment = garments.get(0);//first garment is top garment for that wear place
				if( ! seenGarments.contains(topGarment)){
					seenGarments.add(topGarment);
				}
			}
		}
		return seenGarments.toString();
	}

	/**
	 * method to skim items, looking for clothes to add them to worn clothes
	 */
	public void takeOnClothes(Object enviroment,int gender) {
		//TODO maybe find 'optimal' clothing by environment("Sonntagskleidung", temperature)
		//f.ex. dirtyness/brokeness by map with min/max
		for (Item item : personItems) {
			if (item instanceof Garment) {
				Garment garment = (Garment) item;
				if(		garment.getGender() == Garment.GENDER_UNKNOWN
					|| 	garment.getGender() == gender	){
					wearGarment(garment);
					return;
				}
			}
		}
	}

	public void wearGarment(Garment garment){
		//check wear places the garment covers against wear places covered by garments worn
		for (String wearPlaceNew : garment.getCoveredWearPlaces()) {
			boolean created = false;
			List<Garment> garmentsWornInWearplace = personClothing.get(wearPlaceNew);
			//create list if not exists
			if(garmentsWornInWearplace == null){
				garmentsWornInWearplace = new ArrayList<Garment>();
				created = true;
			}
			addGarmentToList(garment, garmentsWornInWearplace);
			if(created){
				personClothing.put(wearPlaceNew, garmentsWornInWearplace);
			}
		}
	}

	protected boolean addGarmentToList(Garment garment,List<Garment> list){
		//check layers of garments worn
		boolean collision = false;
		for (Garment garmentWorn : list) {
			if(garmentWorn.getLayer().equals(garment.getLayer())){
				logger.warn("Layer collision "+garmentWorn.getLayer()+" with "+garmentWorn+
						" while adding garment "+garment);
				collision = true;
				Double newLayer = garment.getLayer()+garment.getLayerShiftIndex();//shift garment layer by own index
				if(newLayer.equals(garment.getLayer())){
					logger.error("garment("+garment+") cannot be worn -> layer collision: "+newLayer);
					return false;
				}
				garment.setLayer(newLayer );
				addGarmentToList(garment, list);//recursive call
			}
		}
		if( ! collision){
			list.add(garment);
			logger.debug("added garment "+garment+" on layer "+garment.getLayer());
		}
		sortListByLayer(list);
		return true;
	}

	protected void sortListByLayer(List<Garment> list) {
		Collections.sort(list);
		//put highest layer first
		Collections.reverse(list);
	}

	public void takeOffGarment(Garment garment){

	}

	public Map<String,List<Garment>> getAllClothes(){
		return new HashMap<String,List<Garment>>(personClothing);
	}

	public void addItems(List<Item> items) {
		personItems.addAll(items);
	}

	public void addItem(Item item){
		personItems.add(item);
	}

	public void removeItem(Item item){
		personItems.remove(item);
	}

	public List<Item> getItems(){
		return new ArrayList<Item>(personItems);
	}






}
